package com.xzakota.minesweeper;

import com.xzakota.minesweeper.db.ConnectionManager;
import com.xzakota.minesweeper.db.RoomDaoImpl;
import com.xzakota.minesweeper.log.XLogger;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 服务端主程序入口类
 *
 * @author Xzakota
 */
public class Server {
    // 服务器配置
    private static final String SERVER_IP = "127.0.0.1";
    private static final int DEFAULT_SERVER_PORT = 8077;
    // 服务端 Socket
    private static ServerSocket serverSocket = null;
    // 服务端实例
    private static final Server SERVER = new Server();

    // 已连接客户端集（用户不一定登录）
    public static final Set<ConnectedUserHandler> USER_SET = new HashSet<>();
    // 在线用户数（用户已登录）
    private static int userOnline = 0;
    // 房间集
    public static final Map<Integer, RoomAtServer> ROOM = new HashMap<>();

    static {
        // 初始化数据库凭据
        ConnectionManager.initCredential();
        // 从数据库添加房间
        ROOM.putAll(RoomDaoImpl.getAllRoom());
    }

    public static void main(String[] args) throws IOException {
        // 捕获全局异常
        Thread.setDefaultUncaughtExceptionHandler((t, e) -> XLogger.error(e.getMessage(), e));

        // 处理异常关闭
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            for (ConnectedUserHandler handler : USER_SET) {
                if (!handler.isInterrupted()) {
                    handler.interrupt();
                    handler.close();
                    handler.closeStream();
                }
            }
        }));

        int port = DEFAULT_SERVER_PORT;
        if (args.length == 1) {
            try {
                port = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                XLogger.error("指定的端口参数错误，将使用默认端口", e);
            }
        }
        SERVER.startServer(port);
    }

    /**
     * 运行服务端
     *
     * @param port 服务端运行端口
     */
    public void startServer(int port) {
        try {
            serverSocket = new ServerSocket(port, 50, InetAddress.getByName(SERVER_IP));
            XLogger.info("服务端 Socket 建立成功，监听端口 " + port);
            while (true) {
                Socket clientSocket = serverSocket.accept();
                if (clientSocket == null) {
                    break;
                }

                ConnectedUserHandler handler = new ConnectedUserHandler(clientSocket);
                XLogger.info("新的客户端 Socket 连接: " + clientSocket.getInetAddress().getHostAddress());
                Runnable runnable = () -> XLogger.info("在线玩家: " + userOnline);
                handler.userSignInListener = () -> {
                    synchronized (SERVER) {
                        userOnline++;
                        runnable.run();
                    }
                };
                handler.userSignOutListener = () -> {
                    synchronized (SERVER) {
                        userOnline--;
                        runnable.run();
                    }
                };
                USER_SET.add(handler);
                handler.start();
            }
            stopServer();
            XLogger.info("服务端 Socket 连接关闭");
        } catch (Exception e) {
            throw new RuntimeException("服务端 Socket 建立失败", e);
        }
    }

    /**
     * 停止服务端
     *
     * @throws IOException 如果关闭服务端套接字时抛出 IO 异常
     */
    public void stopServer() throws IOException {
        if (serverSocket != null) {
            serverSocket.close();
        }
    }

    /**
     * @return 获取在线用户数
     */
    public static int getOnlineUsers() {
        return userOnline;
    }
}
